package ar.uba.is2.common.mensaje.contenido;

import java.io.StringReader;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Scanner;
import java.util.regex.Pattern;

import org.apache.commons.lang.builder.HashCodeBuilder;
import org.apache.commons.lang.builder.ToStringBuilder;
import org.apache.log4j.Logger;

import ar.uba.is2.common.mensaje.Contenido;
import ar.uba.is2.common.mensaje.ContenidoParticionable;
import ar.uba.is2.common.mensaje.DatoSensado;

public class ContenidoDatosSensados implements ContenidoParticionable {

	private static final Logger logger = Logger.getLogger(ContenidoDatosSensados.class);
	
	private static final Pattern DEFAULT_DELIMITER = Pattern.compile(",");
	
	private static final Pattern DATO_PATTERN = Pattern.compile("<.*?>");

	private List<DatoSensado> datos = new ArrayList<DatoSensado>();
	
	public void addDatoSensado(DatoSensado dato) {
		this.datos.add(dato);
	}
	
	@Override
	public void deserializar(String serializedContent) {
		try {
			
			if (!serializedContent.startsWith("[") && serializedContent.endsWith("]")) {
				throw new RuntimeException(String.format("el string %s no puede deserializarse con %s",serializedContent, this.getClass()));
			}
			
			String datosStream = serializedContent.substring(1, serializedContent.length() - 1);
			
			Scanner scanner = new Scanner(new StringReader(datosStream));
			scanner.useDelimiter(DEFAULT_DELIMITER);
			
			List<DatoSensado> datos = new ArrayList<DatoSensado>();
			String datoStream = null;
			while ((datoStream = scanner.findInLine(DATO_PATTERN)) != null) {
				DatoSensado dato = DatoSensado.deserializar(datoStream);
				datos.add(dato);
			}
			
			this.datos = datos;
			
		} catch (Exception e) {
			logger.warn(String.format("Error al serializar contenido %s", serializedContent), e);
			
		}
	}

	@Override
	public boolean equals(Object obj) {
		
		if ( !(obj instanceof ContenidoDatosSensados) ) {
			return false;
		}
		
		ContenidoDatosSensados that = (ContenidoDatosSensados) obj;
		
		boolean sameSize = this.datos.size() == that.datos.size();
		
		if (!sameSize)
			return false;
		
		return this.datos.containsAll(that.datos);
	}

	@Override
	public List<DatoSensado> getContenido() {
		return this.datos;
	}

	@Override
	public int hashCode() {
		return new HashCodeBuilder().append(this.datos).toHashCode();
	}

	
	@Override
	public void juntarContenido(ContenidoParticionable contenido) {
		if (contenido instanceof ContenidoDatosSensados) {
			this.datos.addAll(((ContenidoDatosSensados)contenido).datos);
		}
	}

	@Override
	public Collection<Contenido> partirContenido(int tamanioMaximo) {

		List<Contenido> contenidoPartido = new ArrayList<Contenido>();
		
		Iterator<DatoSensado> it = this.datos.iterator(); 
		while (it.hasNext()) {

			ContenidoDatosSensados parteContenido = new ContenidoDatosSensados();
			while (it.hasNext() && noMePaso(parteContenido, tamanioMaximo)) {
				parteContenido.datos.add(it.next());
			}

			contenidoPartido.add(parteContenido);
		}

		return contenidoPartido;
	}
	
	@Override
	public String serializar() {
		
		StringBuilder sb = new StringBuilder();
		
		sb.append("[");

		Iterator<DatoSensado> it = this.datos.iterator();
		while (it.hasNext()) {
			
			sb.append(DatoSensado.serializar(it.next()));
			
			if (it.hasNext()) {
				sb.append(",");
			}
		}
		
		sb.append("]");
		
		return sb.toString();
	}
	
	@Override
	public String toString() {
		return new ToStringBuilder(this).append("Datos:").append(this.datos).toString();
	}

	
	/**
	 * tiene en cuenta que para pasarse, debe haber algun datos sensado y toma
	 * el largo del dato anterior ya que se suponene que son todos del mismo
	 * tamano.
	 * 
	 * fix: al usar conjuntos me jode el List#get(int)
	 * entonces tomo el primer dato en vez del último, total supone que son todos iguales
	 * 
	 * @param mensaje
	 * @return
	 */
	private boolean noMePaso(ContenidoDatosSensados contenido, int tamanioMaximo) {
		int cantidadDatos = contenido.getContenido().size();
		
		if ( cantidadDatos == 0 ) {
			return true;
		}
		
		int sizeMensaje = contenido.serializar().length();
		int sizeOtroDato = contenido.getContenido().iterator().next().toString()
				.length();

		return sizeMensaje + sizeOtroDato < tamanioMaximo;
	}
}
